from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from django_redis import get_redis_connection
import base64
import pickle

from .serializers import CartsItemsAddSerializer, CartsItemsGetSerializer, CartsItemsDeleteSerializer, \
    CartsSelectedAllSerializer
from goods.models import SKU


class CartsItemsView(APIView):
    """购物车商品CURD"""

    def perform_authentication(self, request):
        # 重写此方法，直接pass；延后drf的认证，当调用request.user 或 request.auth时才去执行认证
        pass

    def get(self, request, *args, **kwargs):
        """获取购物车商品"""
        try:
            user = request.user
        except:
            user = None

        if user and user.is_authenticated:
            # 登录用户从redis中获取购物车数据
            redis_conn = get_redis_connection('default')
            cart_sku = redis_conn.hgetall('cart_{}'.format(user.id))
            cart_selected = redis_conn.smembers('cart_selected_{}'.format(user.id))
            # {b'14': b'1', b'7': b'1'}
            # {b'7', b'14'}
            # 格式化取出的数据, 和cookie保存的数据格式保持一致
            cart_dict = {}
            for sku_id_byte, sku_count_byte in cart_sku.items():
                cart_dict[int(sku_id_byte)] = {
                    'count': int(sku_count_byte),
                    'selected': sku_id_byte in cart_selected
                }
        else:
            # 未登录用户从cookie中获取购物车数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response({'message': '购物车没有商品'}, status=status.HTTP_400_BAD_REQUEST)

        # 反序列化数据
        # 根据sku_id 查询sku模型数据
        skus = SKU.objects.filter(id__in=cart_dict.keys())
        # 想反序列化的数据中添加模型中没有的字段count， selected
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']
        serializers = CartsItemsGetSerializer(instance=skus, many=True)

        return Response(serializers.data)

    def post(self, request, *args, **kwargs):
        """添加购物车商品"""

        try:
            # 会执行认证逻辑,如果登录用户认证会成功没有异常,但是未登录用户认证会出异常自己拦截
            user = request.user
        except:
            user = None

        # 序列化
        serializers = CartsItemsAddSerializer(data=request.data)
        # 验证
        serializers.is_valid(raise_exception=True)
        # 获取校验后数据
        sku_id = serializers.validated_data.get('sku_id')
        count = serializers.validated_data.get('count')
        selected = serializers.validated_data.get('selected')

        # 响应对象
        response = Response(serializers.data, status=status.HTTP_201_CREATED)
        # 判断用户是否登录(is_authenticated属性表示认证是否通过，匿名用户)
        if user and user.is_authenticated:
            # 登录用户，购物车数据存放到redis
            # Set： {sku_id_1, sku_id_2, ...} 存放状态勾选了的商品，未勾选的商品不存放
            # Hash：{sku_id_1：count, sku_id_2：count} 存放商品id：商品的数量
            redis_conn = get_redis_connection('default')
            pl = redis_conn.pipeline()
            # sku_id和count数量存放 hash中
            pl.hincrby('cart_{}'.format(user.id), sku_id, count)
            # selected是否选中存放到set集合中
            if selected:
                pl.sadd('cart_selected_{}'.format(user.id), sku_id)
            pl.execute()
        else:
            # 未登录用户，购物车数据存放到cookie
            # {
            #     sku_id_1: {count: 1, selected: True},
            #     sku_id_2: {count: 2, selected: False},
            # }

            # 先获取cookie中的数据
            carts_str = request.COOKIES.get('carts')
            if carts_str:
                # str_byt = carts_str.encode()
                # pik_byt = base64.b64decode(str_byt)
                # cart_dict = pickle.loads(pik_byt)
                cart_dict = pickle.loads(base64.b64decode(carts_str.encode()))
            else:
                cart_dict = {}

            # 增量添加商品数量
            if sku_id in cart_dict.keys():
                # 取出原有的商品count数量
                origin_count = cart_dict[sku_id]['count']
                count += origin_count

            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 存放到cookie中
            # byt = pickle.dumps(cart_dict)
            # b64_byt = base64.b64encode(byt)
            # carts_str = b64_byt.decode()
            carts_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie
            response = Response(serializers.data, status=status.HTTP_201_CREATED)
            response.set_cookie('carts', carts_str)
        return response

    def put(self, request, *args, **kwargs):
        """修改购物车商品"""

        try:
            user = request.user
        except:
            user = None

        serializers = CartsItemsAddSerializer(data=request.data)
        serializers.is_valid(raise_exception=True)

        sku_id = serializers.validated_data.get('sku_id')
        count = serializers.validated_data.get('count')
        selected = serializers.validated_data.get('selected')

        response = Response(serializers.data)
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('default')
            pl = redis_conn.pipeline()
            # 覆盖sku_id,count
            pl.hset('cart_{}'.format(user.id), sku_id, count)

            if selected:
                pl.sadd('cart_selected_{}'.format(user.id), sku_id)
            else:
                pl.srem('cart_selected_{}'.format(user.id), sku_id)

            pl.execute()

        else:
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response({'message': '没有获取到cookie'}, status=status.HTTP_400_BAD_REQUEST)

            # 覆盖原有数据
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('carts', cart_str)
        return response

    def delete(self, request, *args, **kwargs):
        """删除购物车商品"""
        try:
            user = request.user
        except:
            user = None

        serializers = CartsItemsDeleteSerializer(data=request.data)
        serializers.is_valid(raise_exception=True)
        sku_id = serializers.validated_data.get('sku_id')

        response = Response(status=status.HTTP_204_NO_CONTENT)
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('default')
            pl = redis_conn.pipeline()
            pl.hdel('cart_{}'.format(user.id), sku_id)
            pl.srem('cart_selected_{}'.format(user.id), sku_id)
            pl.execute()
        else:
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response({'message': '没有获取到cookie'}, status=status.HTTP_400_BAD_REQUEST)

            if sku_id in cart_dict.keys():
                del cart_dict[sku_id]

            # 判断cookie字典中是否还有商品
            if len(cart_dict.keys()):
                cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                response.set_cookie('carts', cart_str)
            else:
                response.delete_cookie('carts')

        return response


class CartsSelectedAllView(APIView):
    """购物车商品全选"""

    def perform_authentication(self, request):
        # 重写此方法，直接pass；延后drf的认证，当调用request.user 或 request.auth时才去执行认证
        pass

    def put(self, request):
        """修改购物车商品全选状态"""
        try:
            user = request.user
        except:
            user = None

        serializers = CartsSelectedAllSerializer(data=request.data)
        serializers.is_valid(raise_exception=True)
        selected = serializers.validated_data.get('selected')

        response = Response(serializers.data)
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('default')
            # 取出hash中的字典，将sku_id 根据selected状态添加或删除set集合中的selected状态
            cart_sku = redis_conn.hgetall('cart_{}'.format(user.id))
            sku_ids = cart_sku.keys()
            if selected:
                redis_conn.sadd('cart_selected_{}'.format(user.id), *sku_ids)
            else:
                redis_conn.srem('cart_selected_{}'.format(user.id), *sku_ids)

        else:
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response({'message': '没有获取到cookie'}, status=status.HTTP_400_BAD_REQUEST)

            for sku_id in cart_dict.keys():
                cart_dict[sku_id]['selected'] = selected

            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            response.set_cookie('carts', cart_str)
        return response
